from pprint import pprint
from packaging import version
import yaml
import re
import os
from pathlib import Path
from dataclasses import dataclass
from typing import Dict, List

CONFIG_BASE_PATH = Path(r'C:\Projects\software-center-config\configs\application') # software-center-config的application本地路径
MANIFEST_BASE_PATH = Path(r'C:\Projects\manifest\build\subsys\rc') # manifest各组件版本本地路径
APP_PATH = Path(r'C:\Projects') # 各组件APP存放本地路径
CHANGELOG_DIFF_OUTPUT_PATH = Path(r'/data') # 组件changelog_diff存放路径
VERSION_FOLDER = '25.06' # 要更新的版本

def get_component_versions():
    result = {}

    # 获取所有组件文件夹
    components = [d for d in os.listdir(CONFIG_BASE_PATH)
                  if os.path.isdir(os.path.join(CONFIG_BASE_PATH, d))]

    for component in components:
        yaml_path = os.path.join(CONFIG_BASE_PATH, component, VERSION_FOLDER, 'software.yml')

        try:
            with open(yaml_path, 'r', encoding='utf-8') as f: 
                data = yaml.safe_load(f)
                version = data.get('app_version')
                if version:
                    result[component] = version
        except Exception as e:
            print(f"Error processing {component}: {str(e)}")

    return result


def extract_new_versions(component_names):
    pattern = re.compile(r'conan:\s*"([^/]+)/([\d.]+)@')
    result = {}

    for yml_file in MANIFEST_BASE_PATH.glob('**/*.yml'):
        try:
            with open(yml_file, 'r', encoding='utf-8') as f:
                content = f.read()
                for match in pattern.finditer(content):
                    comp, ver = match.groups()
                    if comp in component_names:
                        result[comp] = ver
        except Exception as e:
            print(f"Error processing {yml_file}: {str(e)}")

    return result


def update_software_versions(old_versions, new_versions):
    update_log = []

    for component, new_ver in new_versions.items():
        old_ver = old_versions.get(component)
        if not old_ver or old_ver == new_ver:
            continue

        yml_path = CONFIG_BASE_PATH / component / VERSION_FOLDER / 'software.yml'
        if not yml_path.exists():
            update_log.append(f"{component}: 文件不存在")
            continue

        try:
            with open(yml_path, 'r+', encoding='utf-8') as f:
                content = f.read()
                # 严格匹配版本号格式（包含可能的b后缀）
                pattern = fr'(?<!\d){re.escape(old_ver)}(b)?(?!\d)'
                updated = re.sub(pattern, new_ver, content)

                if updated != content:
                    f.seek(0)
                    f.write(updated)
                    f.truncate()
                    update_log.append(f"{component}: {old_ver} → {new_ver}")
                else:
                    update_log.append(f"{component}: 未找到旧版本")
        except Exception as e:
            update_log.append(f"{component} 处理失败: {str(e)}")

    return update_log


@dataclass
class ProcessResult:
    component: str
    status: str  # 'success'/'failed'
    old_version: str
    new_version: str
    error_msg: str = ''
    output_path: str = ''

def find_closest_version(target_ver, all_versions):
    """如果changelog里没有最新目标版本的更新内容，找到小于目标版本的最接近版本"""
    target = version.parse(target_ver)
    candidates = [v for v in all_versions if version.parse(v) < target]
    return max(candidates, key=version.parse) if candidates else None

def find_closest_higher_version(base_ver, all_versions):
    """如果changelog里没有最老版本的更新内容，找到大于基准版本的最接近版本"""
    base = version.parse(base_ver)
    candidates = [v for v in all_versions if version.parse(v) > base]
    return min(candidates, key=version.parse) if candidates else None

def extract_version_changelog(old_versions: Dict, new_versions: Dict) -> List[ProcessResult]:
    CHANGELOG_DIFF_OUTPUT_PATH.mkdir(exist_ok=True)
    results = []

    for component, new_ver in new_versions.items():
        result = ProcessResult(
            component=component,
            status='failed',
            old_version=old_versions.get(component, ''),
            new_version=new_ver
        )

        # 跳过条件检查
        if not result.old_version or result.old_version == new_ver:
            result.error_msg = '版本未变更'
            results.append(result)
            continue

        # 文件存在性检查
        changelog_path = next((f for f in (APP_PATH / component).iterdir()
                               if 'CHANGELOG' in f.name), None)
        if not changelog_path:
            result.error_msg = f'CHANGELOG及相关文件不存在于 {changelog_path}'
            results.append(result)
            continue

        try:
            with open(changelog_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # 提取所有版本号用于回退匹配
            version_pattern = r'## \[([\d.]+)\]'
            all_versions = re.findall(version_pattern, content)

            # 处理start_tag版本是否存在逻辑
            start_ver = new_ver
            if new_ver not in all_versions:
                start_ver = find_closest_version(new_ver, all_versions)
                if not start_ver:
                    continue

            # 处理end_tag版本是否存在逻辑
            end_ver = result.old_version
            fallback_end_ver = None
            if result.old_version not in all_versions:
                fallback_end_ver = find_closest_higher_version(result.old_version, all_versions)
                if not fallback_end_ver:
                    continue

            # 版本区块匹配
            start_tag = f'## [{start_ver}]'
            end_tag = f'## [{end_ver}]' if not fallback_end_ver else f'## [{fallback_end_ver}]'
            pattern = fr'({re.escape(start_tag)}.*?\n.*?){re.escape(end_tag)}'
            match = re.search(pattern, content, re.DOTALL)

            if not match:
                result.error_msg = '版本区间未匹配到内容'
                results.append(result)
                continue

            # 写入输出文件
            output_path = CHANGELOG_DIFF_OUTPUT_PATH / f'{component}_diff2506'
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write(match.group(1).strip())

            result.status = 'success'
            result.output_path = str(output_path)

        except PermissionError:
            result.error_msg = '文件权限不足'
        except UnicodeDecodeError:
            result.error_msg = '文件编码格式异常'
        except Exception as e:
            result.error_msg = f'未知错误: {str(e)}'

        results.append(result)

    return results


if __name__ == '__main__':
    # 获取各组件老版本号
    versions = get_component_versions()
    # 获取各组件新版本号
    new_versions = extract_new_versions(versions.keys())
    # 提取老版本到新版本间的changelog修改
    res = extract_version_changelog(versions, new_versions)
    pprint(res)
    # 更新各组件版本
    log = update_software_versions(versions, new_versions)
    pprint(log)

